﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using FluentNHibernate;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;

namespace BookingSystem.NHibernate
{
    public static class SessionManager
    {
        private static readonly ISessionFactory SessionFactory;

        private static string _dbFile = @"BookingSystem.db";

        [ThreadStatic] private static IDictionary<ISessionFactory, ISession> _context;

        static SessionManager()
        {
            if (IsHttpContext)
            {
                _dbFile = HttpContext.Current.Server.MapPath("~/" + _dbFile);
            }

            SessionFactory = CreateFactory();
        }

        private static bool IsHttpContext
        {
            get { return HttpContext.Current != null; }
        }

        private static ISessionFactory CreateFactory()
        {
//            var config = new Configuration();
//            var persistenceModel = new PersistenceModel();
//            persistenceModel.AddMappingsFromAssembly(typeof(SessionManager).Assembly);
//            persistenceModel.Configure(config);
//
//            return config.BuildSessionFactory();

            return Fluently.Configure()
                .Database(
                    SQLiteConfiguration.Standard
                        .UsingFile(_dbFile).ShowSql()
                )
                .Mappings(m =>
                          m.FluentMappings.AddFromAssemblyOf<Config>())
                .ExposeConfiguration(BuildSchema)
                .BuildSessionFactory();
        }

        private static void BuildSchema(Configuration config)
        {
            //If database file dosent exits, create a new from Schema
          
            if (!File.Exists(_dbFile))
                new SchemaExport(config)
                    .Create(false, true);
        }

        private static void BindSession(ISession session)
        {
            if (IsHttpContext)
            {
                if (HttpContext.Current.Items["NHibernateSession"] == null)
                    HttpContext.Current.Items["NHibernateSession"] = session;
                return;
            }

            DoBind(session, SessionFactory);
        }

        private static void DoBind(ISession current, ISessionFactory factory)
        {
            _context = _context ?? new Dictionary<ISessionFactory, ISession>();
            _context.Add(factory, current);
        }

        public static ISession Unbind()
        {
            return DoUnbind(SessionFactory, true);
        }

        private static ISession DoUnbind(ISessionFactory factory, bool releaseMapIfEmpty)
        {
            ISession session = null;

            if (IsHttpContext)
            {
                if (HttpContext.Current.Items["NHibernateSession"] != null)
                {
                    session = (ISession) HttpContext.Current.Items["NHibernateSession"];
                    HttpContext.Current.Items["NHibernateSession"] = null;
                    return session;
                }
            }

            if (_context != null)
            {
                if (_context.ContainsKey(factory))
                {
                    session = _context[factory];
                    _context.Remove(factory);
                }

                if (releaseMapIfEmpty && _context.Count == 0)

                    _context = null;

                return session;
            }

            return null;
        }

        private static ISession ExistingSession(ISessionFactory factory)
        {
            if (IsHttpContext)
            {
                return (ISession) HttpContext.Current.Items["NHibernateSession"];
            }

            if (_context == null)
                return null;

            if (_context.ContainsKey(factory))
                return _context[factory];

            return null;
        }

        public static ISession CurrentSession()
        {
            ISession current = ExistingSession(SessionFactory);
            if (current == null)
            {
                log4net.Config.XmlConfigurator.Configure();

                current = BuildOrObtainSession();

                // then bind it
                BindSession(current);
            }
            return current;
        }

        private static ISession BuildOrObtainSession()
        {
            return SessionFactory.OpenSession();
        }
    }
}